#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ARRAY_SIZE(a)		(sizeof((a)) / sizeof((a)[0]))
#define MB_TO_BLOCKS(a)		((a) * 2048)

#define UBOOT_SIZE		12
#define LBOOT_SIZE		16		// main partition 1
#define ABOOT_SIZE		16		// main partition 2
#define EXTEN_SIZE		-1		// extend partition 3
#define IDISK_SIZE		-1		// main partition 4


#define MBR_TABLE_OFFSET	0x1BE
#define MBR_MAGIC_OFFSET1	0x1FE
#define MBR_MAGIC_OFFSET2	0x1FF
#define MBR_BLOCK_SIZE		512

#define MBR_FLAG_EXT	0x05
#define MBR_FLAG_LINUX	0x83
#define MBR_FLAG_FAT32	0x0C

typedef struct dos_partition {
	unsigned char boot_ind;		/* 0x80 - active			*/
	unsigned char head;			/* starting head			*/
	unsigned char sector;		/* starting sector			*/
	unsigned char cyl;			/* starting cylinder			*/
	unsigned char sys_ind;		/* What partition type			*/
	unsigned char end_head;		/* end head				*/
	unsigned char end_sector;	/* end sector				*/
	unsigned char end_cyl;		/* end cylinder				*/
	unsigned int start;			/* starting sector counting from 0	*/
	unsigned int size;			/* nr of sectors in partition		*/
} dos_partition_t;

static const dos_partition_t s_part = {
	0x00, 0x03, 0xD0, 0xFF, 0x83, 0x03, 0xD0, 0xFF, 0x0, 0x0
};

static int hw_size[] = {
	12,			// /dev/mmcblk3
	16,			// /dev/mmcblk3p1	-- Linux bootimage
	16,			// /dev/mmcblk3p2	-- Android bootimage
	-1,			// /dev/mmcblk3p3	-- extend partition
	16,			// /dev/mmcblk3p5	-- Android recovery
	256,		// /dev/mmcblk3p6	-- Linux rootfs
	128,		// /dev/mmcblk3p7	-- Linux appfs
	3073,		// /dev/mmcblk3p8	-- Android data
	1537,		// /dev/mmcblk3p9	-- Android system
	512,		// /dev/mmcblk3p10	-- Android cache
	8,			// /dev/mmcblk3p11	-- Android device
	4,			// /dev/mmcblk3p12	-- Android misc
	2,			// /dev/mmcblk3p13	-- Android data footer
	2,			// /dev/mmcblk3p14	-- Android meta data
	40,			// seprate space in extend partition and sdcard partition
	-1,			// /dev/mmcblk3p4	-- Linux/Android internal virtual sdcard
};

static void part_init(unsigned char *p, int sz)
{
	for(int i = 0; i < sz; ++i)
		memcpy(p + i * 0x10, &s_part, 0x10);
}

int main(int argc, char *argv[])
{
	char *fn;
	int asz = 3072;
	if(argc < 2) {
		printf("Usage : %s [-s val] filename\n"
			   "    -s val  -- set android data partition size in MByte(default is 3072M).\n",
				argv[0]);
		return 1;
	}

	if(argc > 3 && !strcmp(argv[1], "-s")) {
		asz = atoi(argv[2]);
		fn = argv[3];
	} else {
		fn = argv[1];
	}

	if(asz > 0) {
		hw_size[7] = asz;
		printf("Create MBR with android data size = %dM\n", asz);
	}

	FILE *pf = fopen(fn, "wb");
	if(!pf) {
		perror("fopen");
		return 1;
	}

	int i;
	unsigned int offset, exbase, exsize, exacc;
	unsigned char mbr[MBR_BLOCK_SIZE];

	bzero(mbr, MBR_BLOCK_SIZE);
	part_init(mbr + MBR_TABLE_OFFSET, 4);
	mbr[MBR_MAGIC_OFFSET1] = 0x55;
	mbr[MBR_MAGIC_OFFSET2] = 0xAA;

	dos_partition_t *p1 = (dos_partition_t*)(mbr + MBR_TABLE_OFFSET + 0x00);
	dos_partition_t *p2 = (dos_partition_t*)(mbr + MBR_TABLE_OFFSET + 0x10);
	dos_partition_t *p3 = (dos_partition_t*)(mbr + MBR_TABLE_OFFSET + 0x20);
	dos_partition_t *p4 = (dos_partition_t*)(mbr + MBR_TABLE_OFFSET + 0x30);

	// main partition 1
	offset = MB_TO_BLOCKS(hw_size[0]);
	p1->start = offset;
	p1->size = MB_TO_BLOCKS(hw_size[1]);

	// main partition 2
	offset += p1->size;
	p2->start = offset;
	p2->size = MB_TO_BLOCKS(hw_size[2]);

	// extend partition 3
	offset += p2->size;
	p3->sys_ind = MBR_FLAG_EXT;
	p3->start = offset;

	i = 4;
	while(hw_size[i] > 0) {
		p3->size += MB_TO_BLOCKS(hw_size[i]);
		++i;
	}
	exbase = offset;
	exsize = p3->size;

	// main partition 4
	offset += p3->size;
	p4->sys_ind = MBR_FLAG_FAT32;
	p4->start = offset;
	p4->size = (unsigned int)-1;

	printf("MP1 S=%08X, L=%08X\n", p1->start, p1->size);
	printf("MP2 S=%08X, L=%08X\n", p2->start, p2->size);
	printf("EP3 S=%08X, L=%08X\n", p3->start, p3->size);
	printf("MP4 S=%08X, L=%08X\n", p4->start, p4->size);

	// write main mbr
	fwrite(mbr, MBR_BLOCK_SIZE, 1, pf);

	// write logic partition table
	i = 4;
	exacc = 0;
	while(hw_size[i] > 0) {
		bzero(mbr, MBR_BLOCK_SIZE);
		part_init(mbr + MBR_TABLE_OFFSET, 2);
		mbr[MBR_MAGIC_OFFSET1] = 0x55;
		mbr[MBR_MAGIC_OFFSET2] = 0xAA;

		p1->start = 1;
		p1->size = MB_TO_BLOCKS(hw_size[i]) - 1;
		p1->sys_ind = MBR_FLAG_LINUX;

		exacc += MB_TO_BLOCKS(hw_size[i]);

		p2->sys_ind = MBR_FLAG_EXT;
		p2->start = exacc;
		p2->size = exsize - exacc;
		fwrite(mbr, MBR_BLOCK_SIZE, 1, pf);
		printf("   [%02d] OFFSET=0x%08X\n", i - 3, exbase + exacc);
		++i;
	}

	fclose(pf);
	return 0;
}

